using Microsoft.Extensions.Logging;
using System.Data;
using System.Data.Common;
using System.Text.Json;

namespace SeataNet.Core.AT.UndoLog
{
    /// <summary>
    /// UndoLog管理器接口
    ///
    /// 定义undo_log表的CRUD操作标准接口。
    ///
    /// 职责：
    /// 1. 第一阶段提交时插入undo_log记录到数据库
    /// 2. 第二阶段提交时删除undo_log记录
    /// 3. 第二阶段回滚时查询undo_log记录
    /// 4. 定期清理过期的undo_log记录
    ///
    /// 工作流程：
    /// ```
    /// 第一阶段（Prepare）：
    /// 1. AtCommandProxy捕获before/after image
    /// 2. 生成UndoLogRecord
    /// 3. 调用InsertUndoLogAsync保存到数据库
    /// 4. 本地事务提交
    ///
    /// 第二阶段提交（Commit）：
    /// 1. TC通知RM提交
    /// 2. 调用DeleteUndoLogAsync删除undo_log
    /// 3. 释放全局锁
    ///
    /// 第二阶段回滚（Rollback）：
    /// 1. TC通知RM回滚
    /// 2. 调用QueryUndoLogAsync查询undo_log
    /// 3. UndoLogExecutor根据undo_log生成反向SQL
    /// 4. 执行反向SQL恢复数据
    /// 5. 调用DeleteUndoLogAsync删除undo_log
    /// ```
    ///
    /// 使用场景：
    /// - AtTransactionProxy.Commit()调用InsertUndoLogAsync
    /// - BranchCommit处理器调用DeleteUndoLogAsync
    /// - BranchRollback处理器调用QueryUndoLogAsync
    /// - 定时任务调用CleanExpiredUndoLogAsync清理过期数据
    ///
    /// 注意事项：
    /// - 所有操作必须在数据库事务中执行
    /// - 插入和删除操作需要与业务SQL在同一个事务中
    /// - 查询操作可以使用独立的连接
    /// </summary>
    public interface IUndoLogManager
    {
        /// <summary>
        /// 插入UndoLog记录
        ///
        /// 在第一阶段提交时调用，将undo_log保存到数据库。
        ///
        /// 执行流程：
        /// 1. 序列化UndoLogRecord为JSON字符串
        /// 2. 生成INSERT SQL语句
        /// 3. 绑定参数：branchId、xid、context、rollbackInfo、logStatus等
        /// 4. 执行INSERT操作
        /// 5. 记录日志
        ///
        /// 调用时机：
        /// - AtTransactionProxy.Commit()提交本地事务后
        /// - 在同一个数据库事务中（确保原子性）
        /// - 业务SQL执行成功后
        ///
        /// 注意事项：
        /// - 必须在同一个事务中执行（与业务SQL）
        /// - rollbackInfo字段存储序列化后的UndoLogRecord
        /// - 插入失败会导致事务回滚
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <param name="undoLogRecord">undo_log记录，包含before/after image</param>
        Task InsertUndoLogAsync(DbConnection connection, string xid, long branchId, UndoLogRecord undoLogRecord);

        /// <summary>
        /// 删除UndoLog记录
        ///
        /// 在第二阶段提交或回滚完成后调用，删除不再需要的undo_log。
        ///
        /// 执行流程：
        /// 1. 生成DELETE SQL语句（WHERE xid = ? AND branch_id = ?）
        /// 2. 绑定参数：xid、branchId
        /// 3. 执行DELETE操作
        /// 4. 记录删除的行数
        ///
        /// 调用时机：
        /// - 第二阶段提交：BranchCommit处理器调用（成功提交后）
        /// - 第二阶段回滚：BranchRollback处理器调用（回滚完成后）
        /// - 可以在独立的数据库连接中执行
        ///
        /// 注意事项：
        /// - 删除操作应该是幂等的（重复删除不报错）
        /// - 如果找不到记录（已删除），不抛出异常
        /// - 删除失败不影响数据一致性（数据已正确提交或回滚）
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        Task DeleteUndoLogAsync(DbConnection connection, string xid, long branchId);

        /// <summary>
        /// 查询UndoLog记录
        ///
        /// 在第二阶段回滚时调用，查询需要回滚的undo_log。
        ///
        /// 执行流程：
        /// 1. 生成SELECT SQL语句（WHERE xid = ? AND branch_id = ?）
        /// 2. 绑定参数：xid、branchId
        /// 3. 执行查询
        /// 4. 遍历结果集，构造UndoLogModel对象列表
        /// 5. 返回查询结果
        ///
        /// 调用时机：
        /// - 第二阶段回滚：BranchRollback处理器调用
        /// - UndoLogExecutor需要根据查询结果生成反向SQL
        /// - 可以在独立的数据库连接中执行
        ///
        /// 注意事项：
        /// - 通常每个分支只有一条undo_log记录
        /// - 返回列表按log_created降序排序（最新的在前）
        /// - 如果找不到记录，返回空列表（可能已被删除或提交）
        /// - 需要反序列化RollbackInfo字段为UndoLogRecord
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <returns>undo_log记录列表，可能为空</returns>
        Task<List<UndoLogModel>> QueryUndoLogAsync(DbConnection connection, string xid, long branchId);

        /// <summary>
        /// 批量删除UndoLog记录
        ///
        /// 批量删除多个全局事务的所有undo_log记录。
        ///
        /// 执行流程：
        /// 1. 检查xids列表不为空
        /// 2. 生成DELETE SQL语句（WHERE xid IN (?, ?, ...)）
        /// 3. 为每个xid绑定参数
        /// 4. 执行批量DELETE操作
        /// 5. 记录删除的行数
        ///
        /// 调用时机：
        /// - 批量提交：删除多个已提交事务的undo_log
        /// - 批量清理：定期清理已完成事务的undo_log
        ///
        /// 使用场景：
        /// - 提高性能：一次删除多个事务的记录
        /// - 批量清理：减少数据库操作次数
        ///
        /// 注意事项：
        /// - xids列表为空时直接返回（不执行SQL）
        /// - 参数数量可能很大，需要注意SQL长度限制
        /// - 建议分批处理（如每次最多1000个xid）
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="xids">全局事务ID列表</param>
        Task BatchDeleteUndoLogAsync(DbConnection connection, List<string> xids);

        /// <summary>
        /// 清理过期UndoLog记录
        ///
        /// 定期清理长时间未删除的undo_log记录（可能是异常事务）。
        ///
        /// 执行流程：
        /// 1. 生成DELETE SQL语句（WHERE log_modified < ?）
        /// 2. 绑定过期时间参数
        /// 3. 执行DELETE操作
        /// 4. 记录删除的行数
        ///
        /// 调用时机：
        /// - 定时任务调用（如每小时执行一次）
        /// - 手动清理（管理员操作）
        ///
        /// 过期判断：
        /// - 根据log_modified字段判断
        /// - 通常保留7天内的记录
        /// - 示例：expireTime = DateTime.Now.AddDays(-7)
        ///
        /// 使用场景：
        /// - 防止undo_log表无限增长
        /// - 清理异常事务的遗留数据
        /// - 释放数据库存储空间
        ///
        /// 注意事项：
        /// - 确保expireTime设置合理（不要误删有效数据）
        /// - 建议在低峰期执行（避免影响性能）
        /// - 可以添加log_status条件（只删除正常状态的记录）
        /// - 防御性undo_log（log_status=1）可能需要人工处理
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="expireTime">过期时间，早于此时间的记录将被删除</param>
        Task CleanExpiredUndoLogAsync(DbConnection connection, DateTime expireTime);
    }

    /// <summary>
    /// UndoLog管理器默认实现
    ///
    /// 职责：
    /// 1. 实现IUndoLogManager接口的所有方法
    /// 2. 管理undo_log表的CRUD操作
    /// 3. 序列化/反序列化UndoLogRecord
    /// 4. 提供参数化查询以避免SQL注入
    ///
    /// 实现特点：
    /// - 使用JSON序列化存储回滚信息
    /// - 使用参数化查询确保安全性
    /// - 支持多种数据库（通过DbConnection抽象）
    /// - 记录详细的操作日志
    ///
    /// 数据库兼容性：
    /// - 支持SQL Server、MySQL、PostgreSQL等
    /// - 使用标准SQL语法（避免数据库特定语法）
    /// - 参数占位符使用@符号（部分数据库可能需要适配）
    ///
    /// 线程安全：
    /// - 无状态实现，线程安全
    /// - 每次操作使用传入的connection
    /// - 不维护内部状态或缓存
    ///
    /// 使用示例：
    /// ```csharp
    /// var manager = new DefaultUndoLogManager(logger);
    ///
    /// // 插入undo_log
    /// using (var connection = CreateConnection())
    /// using (var transaction = connection.BeginTransaction())
    /// {
    ///     // 执行业务SQL
    ///     ExecuteBusinessSql(connection, transaction);
    ///
    ///     // 插入undo_log（同一个事务）
    ///     await manager.InsertUndoLogAsync(connection, xid, branchId, undoLogRecord);
    ///
    ///     transaction.Commit();
    /// }
    ///
    /// // 查询undo_log
    /// using (var connection = CreateConnection())
    /// {
    ///     var logs = await manager.QueryUndoLogAsync(connection, xid, branchId);
    ///     // 处理查询结果
    /// }
    ///
    /// // 删除undo_log
    /// using (var connection = CreateConnection())
    /// {
    ///     await manager.DeleteUndoLogAsync(connection, xid, branchId);
    /// }
    /// ```
    ///
    /// 注意事项：
    /// - UNDO_LOG_TABLE常量定义表名
    /// - 所有操作都记录日志
    /// - 使用AddParameter辅助方法添加参数
    /// </summary>
    public class DefaultUndoLogManager : IUndoLogManager
    {
        private readonly ILogger<DefaultUndoLogManager> _logger;
        // undo_log表名常量
        private const string UNDO_LOG_TABLE = "undo_log";

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger">日志记录器</param>
        public DefaultUndoLogManager(ILogger<DefaultUndoLogManager> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 插入UndoLog记录到数据库
        ///
        /// 执行流程：
        /// 1. 记录调试日志
        /// 2. 将UndoLogRecord序列化为JSON字符串
        /// 3. 构造INSERT SQL语句
        /// 4. 创建DbCommand并设置参数：
        ///    - @BranchId: 分支事务ID
        ///    - @Xid: 全局事务ID
        ///    - @Context: 事务上下文（当前为空字符串）
        ///    - @RollbackInfo: 序列化后的UndoLogRecord（JSON）
        ///    - @LogStatus: 日志状态（0=正常，1=防御性）
        ///    - @LogCreated: 创建时间（当前时间）
        ///    - @LogModified: 修改时间（当前时间）
        /// 5. 执行INSERT操作
        /// 6. 记录成功日志
        ///
        /// 注意事项：
        /// - 必须在同一个数据库事务中调用（与业务SQL）
        /// - rollbackInfo使用JsonSerializer.Serialize序列化
        /// - log_status默认为0（正常状态）
        /// - log_created和log_modified设置为当前时间
        /// - 插入失败会抛出异常，导致事务回滚
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开并在事务中</param>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <param name="undoLogRecord">undo_log记录，包含表名、SQL类型、before/after image</param>
        public async Task InsertUndoLogAsync(DbConnection connection, string xid, long branchId, UndoLogRecord undoLogRecord)
        {
            _logger.LogDebug($"插入UndoLog: XID={xid}, BranchId={branchId}");

            // 序列化回滚信息
            var rollbackInfo = JsonSerializer.Serialize(undoLogRecord);

            var sql = $@"INSERT INTO {UNDO_LOG_TABLE}
                (branch_id, xid, context, rollback_info, log_status, log_created, log_modified)
                VALUES
                (@BranchId, @Xid, @Context, @RollbackInfo, @LogStatus, @LogCreated, @LogModified)";

            using var command = connection.CreateCommand();
            command.CommandText = sql;
            command.CommandType = CommandType.Text;

            AddParameter(command, "@BranchId", branchId);
            AddParameter(command, "@Xid", xid);
            AddParameter(command, "@Context", "");
            AddParameter(command, "@RollbackInfo", rollbackInfo);
            AddParameter(command, "@LogStatus", 0);
            AddParameter(command, "@LogCreated", DateTime.Now);
            AddParameter(command, "@LogModified", DateTime.Now);

            await command.ExecuteNonQueryAsync();

            _logger.LogDebug($"UndoLog插入成功: XID={xid}, BranchId={branchId}");
        }

        /// <summary>
        /// 删除UndoLog记录
        ///
        /// 执行流程：
        /// 1. 记录调试日志
        /// 2. 构造DELETE SQL语句（WHERE xid = @Xid AND branch_id = @BranchId）
        /// 3. 创建DbCommand并设置参数：
        ///    - @Xid: 全局事务ID
        ///    - @BranchId: 分支事务ID
        /// 4. 执行DELETE操作
        /// 5. 获取受影响的行数
        /// 6. 记录删除完成日志（包含影响行数）
        ///
        /// 幂等性：
        /// - 如果记录不存在（已删除），affectedRows=0，不抛出异常
        /// - 可以安全地重复调用
        ///
        /// 注意事项：
        /// - 删除操作通常在独立的事务中执行
        /// - 不检查记录是否存在（直接执行DELETE）
        /// - 如果affectedRows=0，说明记录可能已被删除或不存在
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        public async Task DeleteUndoLogAsync(DbConnection connection, string xid, long branchId)
        {
            _logger.LogDebug($"删除UndoLog: XID={xid}, BranchId={branchId}");

            var sql = $"DELETE FROM {UNDO_LOG_TABLE} WHERE xid = @Xid AND branch_id = @BranchId";

            using var command = connection.CreateCommand();
            command.CommandText = sql;
            command.CommandType = CommandType.Text;

            AddParameter(command, "@Xid", xid);
            AddParameter(command, "@BranchId", branchId);

            var affectedRows = await command.ExecuteNonQueryAsync();

            _logger.LogDebug($"UndoLog删除完成: XID={xid}, BranchId={branchId}, 影响行数={affectedRows}");
        }

        /// <summary>
        /// 查询UndoLog记录
        ///
        /// 执行流程：
        /// 1. 记录调试日志
        /// 2. 构造SELECT SQL语句，查询所有字段并按log_created降序排序
        /// 3. 创建DbCommand并设置参数：
        ///    - @Xid: 全局事务ID
        ///    - @BranchId: 分支事务ID
        /// 4. 执行查询并获取DataReader
        /// 5. 遍历结果集，为每一行创建UndoLogModel对象：
        ///    - 读取7个字段：branch_id, xid, context, rollback_info, log_status, log_created, log_modified
        ///    - 按顺序从reader中读取对应类型的值
        ///    - 添加到结果列表中
        /// 6. 记录查询到的记录数量
        /// 7. 返回结果列表
        ///
        /// 调用时机：
        /// - 第二阶段回滚：BranchRollback处理器调用
        /// - UndoLogExecutor需要根据查询结果生成反向SQL并执行
        /// - 可以在独立的数据库连接中执行（不需要与业务SQL在同一事务）
        ///
        /// 查询结果说明：
        /// - 结果按log_created DESC排序，最新的记录在前
        /// - 通常每个分支只有一条undo_log记录
        /// - 如果有多条记录（异常情况），使用最新的一条
        /// - RollbackInfo字段包含JSON序列化的UndoLogRecord对象
        ///
        /// 注意事项：
        /// - 如果找不到记录，返回空列表（不抛出异常）
        /// - 可能的原因：undo_log已被删除、事务已提交、XID或BranchId错误
        /// - RollbackInfo需要后续反序列化为UndoLogRecord对象
        /// - 查询操作不会修改数据库，可以安全地重复调用
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <returns>undo_log记录列表，可能为空但不会为null</returns>
        public async Task<List<UndoLogModel>> QueryUndoLogAsync(DbConnection connection, string xid, long branchId)
        {
            _logger.LogDebug($"查询UndoLog: XID={xid}, BranchId={branchId}");

            var sql = $@"SELECT branch_id, xid, context, rollback_info, log_status, log_created, log_modified
                FROM {UNDO_LOG_TABLE}
                WHERE xid = @Xid AND branch_id = @BranchId
                ORDER BY log_created DESC";

            using var command = connection.CreateCommand();
            command.CommandText = sql;
            command.CommandType = CommandType.Text;

            AddParameter(command, "@Xid", xid);
            AddParameter(command, "@BranchId", branchId);

            var result = new List<UndoLogModel>();

            using var reader = await command.ExecuteReaderAsync();
            while (await reader.ReadAsync())
            {
                result.Add(new UndoLogModel
                {
                    BranchId = reader.GetInt64(0),
                    Xid = reader.GetString(1),
                    Context = reader.GetString(2),
                    RollbackInfo = reader.GetString(3),
                    LogStatus = reader.GetInt32(4),
                    LogCreated = reader.GetDateTime(5),
                    LogModified = reader.GetDateTime(6)
                });
            }

            _logger.LogDebug($"查询到{result.Count}条UndoLog: XID={xid}, BranchId={branchId}");
            return result;
        }

        /// <summary>
        /// 批量删除UndoLog记录
        ///
        /// 执行流程：
        /// 1. 检查xids列表是否为null或空，如果是则直接返回
        /// 2. 记录调试日志，包含要删除的事务数量
        /// 3. 构造参数化的DELETE SQL语句：
        ///    - 使用IN子句：WHERE xid IN (@Xid0, @Xid1, @Xid2, ...)
        ///    - 为每个xid生成唯一的参数名（@Xid0, @Xid1等）
        /// 4. 创建DbCommand并设置参数：
        ///    - 遍历xids列表，为每个xid添加参数
        ///    - 参数名格式：@Xid{i}，其中i是索引
        /// 5. 执行批量DELETE操作
        /// 6. 获取受影响的行数
        /// 7. 记录删除完成日志（包含影响行数）
        ///
        /// 调用时机：
        /// - 批量提交：第二阶段提交时，删除多个已提交事务的undo_log
        /// - 批量清理：定期清理已完成事务的undo_log
        /// - 性能优化：减少数据库操作次数，一次删除多个事务的记录
        ///
        /// 性能优化：
        /// - 相比逐个删除，批量删除大幅减少数据库往返次数
        /// - 一次SQL执行删除多条记录，提高吞吐量
        /// - 减少数据库连接资源的占用
        ///
        /// 注意事项：
        /// - xids列表为空时直接返回，不执行SQL（避免语法错误）
        /// - 参数数量可能很大，需要注意SQL长度限制（建议每次最多1000个xid）
        /// - 建议分批处理：如果xids数量超过1000，分多次调用此方法
        /// - affectedRows可能小于xids.Count（部分undo_log已被删除）
        /// - 操作是幂等的，重复调用不会有副作用
        /// - 不同数据库对参数数量可能有不同限制（如SQL Server最多2100个参数）
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="xids">全局事务ID列表，要批量删除的事务集合</param>
        public async Task BatchDeleteUndoLogAsync(DbConnection connection, List<string> xids)
        {
            if (xids == null || !xids.Any())
                return;

            _logger.LogDebug($"批量删除UndoLog: 数量={xids.Count}");

            var xidParams = string.Join(",", xids.Select((x, i) => $"@Xid{i}"));
            var sql = $"DELETE FROM {UNDO_LOG_TABLE} WHERE xid IN ({xidParams})";

            using var command = connection.CreateCommand();
            command.CommandText = sql;
            command.CommandType = CommandType.Text;

            for (int i = 0; i < xids.Count; i++)
            {
                AddParameter(command, $"@Xid{i}", xids[i]);
            }

            var affectedRows = await command.ExecuteNonQueryAsync();

            _logger.LogDebug($"批量删除UndoLog完成: 影响行数={affectedRows}");
        }

        /// <summary>
        /// 清理过期UndoLog记录
        ///
        /// 执行流程：
        /// 1. 记录调试日志，包含过期时间参数
        /// 2. 构造DELETE SQL语句（WHERE log_modified < @ExpireTime）
        /// 3. 创建DbCommand并设置参数：
        ///    - @ExpireTime: 过期时间阈值
        /// 4. 执行DELETE操作
        /// 5. 获取受影响的行数（删除的记录数）
        /// 6. 记录清理完成日志（包含影响行数）
        ///
        /// 调用时机：
        /// - 定时任务调用：通常每小时或每天执行一次
        /// - 手动清理：管理员通过管理界面或脚本触发
        /// - 应用启动时：清理启动前的过期数据
        ///
        /// 过期时间设置示例：
        /// ```csharp
        /// // 清理7天前的undo_log
        /// var expireTime = DateTime.Now.AddDays(-7);
        /// await manager.CleanExpiredUndoLogAsync(connection, expireTime);
        ///
        /// // 清理1小时前的undo_log（更激进的策略）
        /// var expireTime = DateTime.Now.AddHours(-1);
        /// await manager.CleanExpiredUndoLogAsync(connection, expireTime);
        /// ```
        ///
        /// 保留时间策略：
        /// - 生产环境建议：保留7天（应对回滚延迟和问题排查）
        /// - 测试环境建议：保留1-3天（减少存储压力）
        /// - 高频场景建议：保留3天但增加清理频率
        ///
        /// 使用场景：
        /// - 防止undo_log表无限增长，占用过多存储空间
        /// - 清理异常事务的遗留数据（如应用崩溃、网络中断导致的孤儿记录）
        /// - 释放数据库存储空间，提高查询性能
        /// - 减少索引大小，加快查询速度
        ///
        /// 注意事项：
        /// - 确保expireTime设置合理，不要误删有效数据
        /// - 建议在低峰期执行（如凌晨），避免影响业务性能
        /// - 可以添加log_status条件，只删除正常状态的记录（log_status=0）
        /// - 防御性undo_log（log_status=1）可能需要人工审核后再删除
        /// - 删除前建议先查询将要删除的记录数（SELECT COUNT(*) WHERE log_modified < ?）
        /// - 大量删除可能导致锁等待，建议分批删除（如每次最多删除10000条）
        /// - 注意log_modified字段的时区问题（建议使用UTC时间）
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <param name="expireTime">过期时间阈值，早于此时间的记录将被删除</param>
        public async Task CleanExpiredUndoLogAsync(DbConnection connection, DateTime expireTime)
        {
            _logger.LogDebug($"清理过期UndoLog: 过期时间={expireTime}");

            var sql = $"DELETE FROM {UNDO_LOG_TABLE} WHERE log_modified < @ExpireTime";

            using var command = connection.CreateCommand();
            command.CommandText = sql;
            command.CommandType = CommandType.Text;

            AddParameter(command, "@ExpireTime", expireTime);

            var affectedRows = await command.ExecuteNonQueryAsync();

            _logger.LogDebug($"清理过期UndoLog完成: 影响行数={affectedRows}");
        }

        /// <summary>
        /// 添加命令参数（辅助方法）
        ///
        /// 创建DbParameter对象并添加到命令的参数集合中。
        ///
        /// 执行流程：
        /// 1. 使用command.CreateParameter()创建参数对象
        /// 2. 设置参数名称（parameterName）
        /// 3. 设置参数值：
        ///    - 如果value为null，设置为DBNull.Value（数据库的NULL值）
        ///    - 否则使用原始值
        /// 4. 将参数添加到命令的Parameters集合中
        ///
        /// NULL值处理：
        /// - C#的null无法直接传递给数据库，需要转换为DBNull.Value
        /// - 使用?? 运算符：value ?? DBNull.Value
        /// - 确保数据库能正确识别NULL值
        ///
        /// 使用场景：
        /// - InsertUndoLogAsync: 添加7个参数（BranchId, Xid, Context等）
        /// - DeleteUndoLogAsync: 添加2个参数（Xid, BranchId）
        /// - QueryUndoLogAsync: 添加2个参数（Xid, BranchId）
        /// - BatchDeleteUndoLogAsync: 添加多个参数（Xid0, Xid1, ...）
        /// - CleanExpiredUndoLogAsync: 添加1个参数（ExpireTime）
        ///
        /// 优点：
        /// - 防止SQL注入：使用参数化查询而非字符串拼接
        /// - 代码复用：统一的参数添加逻辑
        /// - 类型安全：由数据库驱动负责类型转换
        /// - 性能优化：数据库可以缓存执行计划
        ///
        /// 注意事项：
        /// - parameterName必须与SQL中的占位符一致（如@Xid）
        /// - 不同数据库的占位符可能不同（SQL Server用@，Oracle用:，MySQL用?）
        /// - 当前实现使用@符号，适用于SQL Server、MySQL（5.0+）、PostgreSQL
        /// - value的类型必须与数据库列类型兼容
        /// </summary>
        /// <param name="command">数据库命令对象</param>
        /// <param name="parameterName">参数名称，需要包含@前缀，如"@Xid"</param>
        /// <param name="value">参数值，可以为null（会自动转换为DBNull.Value）</param>
        private void AddParameter(DbCommand command, string parameterName, object value)
        {
            var parameter = command.CreateParameter();
            parameter.ParameterName = parameterName;
            parameter.Value = value ?? DBNull.Value;
            command.Parameters.Add(parameter);
        }
    }
}
